package com.cn.gps;

import com.alibaba.fastjson.JSON;

import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Created by chenyuncong on 2018/8/29.
 */
public class GeoUtils {

    /**
     * 地球半径
     */
    private static double EARTH_RADIUS = 6378138.0;

    private static double rad(double d)
    {
        return d * Math.PI / 180.0;
    }

    public static double radius( double acreage){
       return Math.sqrt(acreage/Math.PI);
    }

    /**
     * 计算是否在圆上
     *
     * @Title: GetDistance
     * @Description: TODO()
     * @param radius 半径  单位(m)
     * @param lat1  纬度
     * @param lng1  经度
     * @return
     * @return double
     * @throws
     */
    public static boolean isInCircle(double radius,double lat1, double lng1, double lat2, double lng2)
    {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
                Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10000;
        if(s > radius) {//不在圆上
            return false;
        }else {
            return true;
        }
    }

    /**
     * 是否在矩形区域内
     * @Title: isInArea
     * @Description: TODO()
     * @param lat 测试点经度
     * @param lng 测试点纬度
     * @param minLat 纬度范围限制1
     * @param maxLat 纬度范围限制2
     * @param minLng 经度限制范围1
     * @param maxLng 经度范围限制2
     * @return
     * @return boolean
     * @throws
     */
    public static boolean isInRectangleArea(double lat,double lng,double minLat,
                                            double maxLat,double minLng,double maxLng){
        if(isInRange(lat, minLat, maxLat)){//如果在纬度的范围内
            if(minLng*maxLng>0){
                if(isInRange(lng, minLng, maxLng)){
                    return true;
                }else {
                    return false;
                }
            }else {
                if(Math.abs(minLng)+Math.abs(maxLng)<180){
                    if(isInRange(lng, minLng, maxLng)){
                        return true;
                    }else {
                        return false;
                    }
                }else{
                    double left = Math.max(minLng, maxLng);
                    double right = Math.min(minLng, maxLng);
                    if(isInRange(lng, left, 180)||isInRange(lng, right,-180)){
                        return true;
                    }else {
                        return false;
                    }
                }
            }
        }else{
            return false;
        }
    }

    /**
     * 判断是否在经纬度范围内
     * @Title: isInRange
     * @Description: TODO()
     * @param point
     * @param left
     * @param right
     * @return
     * @return boolean
     * @throws
     */
    public static boolean isInRange(double point, double left,double right){
        if(point>=Math.min(left, right)&&point<=Math.max(left, right)){
            return true;
        }else {
            return false;
        }
    }

    /**
     * 判断点是否在多边形内
     * @Title: IsPointInPoly
     * @Description: TODO()
     * @param point 测试点
     * @param pts 多边形的点
     * @return
     * @return boolean
     * @throws
     */
    public static boolean isInPolygon(Point2D.Double point, List<Point2D.Double> pts){

        int N = pts.size();
        boolean boundOrVertex = true;
        int intersectCount = 0;//交叉点数量
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        Point2D.Double p1, p2;//临近顶点
        Point2D.Double p = point; //当前点

        p1 = pts.get(0);
        for(int i = 1; i <= N; ++i){
            if(p.equals(p1)){
                return boundOrVertex;
            }

            p2 = pts.get(i % N);
            if(p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)){
                p1 = p2;
                continue;
            }

            //射线穿过算法
            if(p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)){
                if(p.y <= Math.max(p1.y, p2.y)){
                    if(p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)){
                        return boundOrVertex;
                    }

                    if(p1.y == p2.y){
                        if(p1.y == p.y){
                            return boundOrVertex;
                        }else{
                            ++intersectCount;
                        }
                    }else{
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if(Math.abs(p.y - xinters) < precision){
                            return boundOrVertex;
                        }

                        if(p.y < xinters){
                            ++intersectCount;
                        }
                    }
                }
            }else{
                if(p.x == p2.x && p.y <= p2.y){
                    Point2D.Double p3 = pts.get((i+1) % N);
                    if(p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)){
                        ++intersectCount;
                    }else{
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }
        if(intersectCount % 2 == 0){//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    private static final double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

    private static final double pi = 3.14159265358979324;
    private static final double a = 6378245.0;
    private static final double ee = 0.00669342162296594323;

    /**
     * gg_lat 纬度
     * gg_lon 经度
     * GCJ-02转换BD-09
     * Google地图经纬度转百度地图经纬度
     * */
    public static Point google_bd_encrypt(double gg_lat, double gg_lon){
        Point point=new Point();
        double x = gg_lon, y = gg_lat;
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double bd_lon = z * Math.cos(theta) + 0.0065;
        double bd_lat = z * Math.sin(theta) + 0.006;
        point.setLat(bd_lat);
        point.setLng(bd_lon);
        return point;
    }

    /**
     * wgLat 纬度
     * wgLon 经度
     * BD-09转换GCJ-02
     * 百度转google
     * */
    public static Point bd_google_encrypt(double bd_lat, double bd_lon){
        Point point=new Point();
        double x = bd_lon - 0.0065, y = bd_lat - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta =Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double gg_lon = z * Math.cos(theta);
        double gg_lat = z * Math.sin(theta);
        point.setLat(gg_lat);
        point.setLng(gg_lon);
        return point;
    }



    /**
     * wgLat 纬度
     * wgLon 经度
     * WGS-84 到 GCJ-02 的转换（即 GPS 加偏）
     * */
    public static Point wgs_gcj_encrypts(double wgLat, double wgLon) {
        Point point=new Point();
        if (outOfChina(wgLat, wgLon)) {
            point.setLat(wgLat);
            point.setLng(wgLon);
            return point;
        }
        double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
        double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
        double radLat = wgLat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        double lat = wgLat + dLat;
        double lon = wgLon + dLon;
        point.setLat(lat);
        point.setLng(lon);
        return point;
    }

    public static double[] Convert_BD09_To_GCJ02(double lat, double lng)
    {
	    double x = lng - 0.0065, y = lat - 0.006;
	    double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
	    double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
	    lng = z * Math.cos(theta);
	    lat = z * Math.sin(theta);
	    double[]  dd = {lat,lng}; 
	    return dd;
    }
    
    public static Point2D.Double gpsToBaidu(double wgLat, double wgLon){
        Point point = wgs_gcj_encrypts(wgLat,wgLon);
        Point point2 = google_bd_encrypt(point.getLat(),point.getLng());
        Point2D.Double poi=new Point2D.Double(point2.getLng(),point2.getLat());
        return poi;
    }

    public static void transform(double wgLat, double wgLon, double[] latlng) {
        if (outOfChina(wgLat, wgLon)) {
            latlng[0] = wgLat;
            latlng[1] = wgLon;
            return;
        }
        double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
        double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
        double radLat = wgLat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        latlng[0] = wgLat + dLat;
        latlng[1] = wgLon + dLon;
    }

    private static boolean outOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347){
            return true;
        }

        if (lat < 0.8293 || lat > 55.8271){
            return true;
        }

        return false;
    }

    private static double transformLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private static double transformLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    public static void main(String[] args) {

        Set<String> set=new HashSet<>();
        set.add("aa");
        set.add("bb");
        set.add("cc");
        	
        System.out.println(JSON.toJSONString(Convert_BD09_To_GCJ02(113.323557,23.126375)));


       /* long statTime=System.currentTimeMillis();
        for(int i=0;i<1000000;i++) {
            Point2D.Double point = new Point2D.Double(113.258268, 23.134006);
            List<Point2D.Double> list = new ArrayList<>();
            list.add(new Point2D.Double(113.262804, 23.136636));
            list.add(new Point2D.Double(113.266829, 23.135007));
            list.add(new Point2D.Double(113.266056, 23.133279));
            list.add(new Point2D.Double(113.263307, 23.133329));
            list.add(new Point2D.Double(113.257486, 23.134077));
            boolean f = isInPolygon(point, list);
            List<Point2D.Double> list2 = new ArrayList<>();
            list2.add(new Point2D.Double(113.282154, 23.141388));
            list2.add(new Point2D.Double(113.284238, 23.131285));
            list2.add(new Point2D.Double(113.267386, 23.129158));
            list2.add(new Point2D.Double(113.267422, 23.136104));
            list2.add(new Point2D.Double(113.269757, 23.141122));
            list2.add(new Point2D.Double(113.278489, 23.143481));
            list2.add(new Point2D.Double(113.280968, 23.143016));
            list2.add(new Point2D.Double(113.280968, 23.143016));
            Point2D.Double point2 = new Point2D.Double(113.266559, 23.128925);
            boolean f2 = isInPolygon(point2, list2);
//            isInCircle(200,113.274824,23.139228, 113.27662,23.139128);
        }
        System.out.println(System.currentTimeMillis()-statTime);*/
//        isInCircle(double radius,double lat1, double lng1, double lat2, double lng2)

//        113.274824,23.139228   113.27662,23.139128
        System.out.println(isInCircle(200,113.274824,23.139228, 113.27662,23.139128));
    }
}
